﻿using System.ComponentModel;
using System.Data;
using System.Dynamic;
using System.Reflection;

namespace DB2WORD.Logics
{
    public static class DataTableExtensions
    {
        public static IEnumerable<dynamic> AsDynamics(this DataTable dataTable)
        {
            if(dataTable == null)
                throw new ArgumentNullException(nameof(dataTable));

            foreach(var row in dataTable.AsDictionaries())
            {
                yield return row;
            }
        }

        public static IEnumerable<IDictionary<string, object>> AsDictionaries(this DataTable dataTable)
        {
            if(dataTable == null)
                throw new ArgumentNullException(nameof(dataTable));

            var cols = dataTable.Columns.OfType<DataColumn>().Select(x => x.ColumnName).ToList();

            foreach(var row in dataTable.AsEnumerable())
            {
                IDictionary<string, object> rst = new ExpandoObject();
                foreach(var col in cols)
                {
                    if(row[col] == DBNull.Value)
                    {
                        rst[col] = null;
                    }
                    else
                    {
                        rst[col] = row[col];
                    }
                }
                yield return rst;
            }
        }
        /// <summary>
        /// 将DataTable转换成List
        /// </summary>
        /// <typeparam name="T">list元素的类型</typeparam>
        /// <param name="table">要转换的DataTable</param>
        /// <returns></returns>
        public static List<T> AsSingleColumn<T>(this DataTable table, Func<object, T> mapper = null)
        {
            if(table == null)
            {
                return null;
            }

            var list = new List<T>();

            if(table.Rows.Count == 0)
            {
                return list;
            }

            if(mapper == null)
            {
                var tc = table.Columns[0].DataType;
                var ty = typeof(T);
                mapper = o => default(T);
                if(tc == ty)
                {
                    mapper = o => (T)o;
                }
                else
                {
                    var c = TypeDescriptor.GetConverter(ty);
                    if(c.CanConvertFrom(tc))
                    {
                        mapper = o => (T)c.ConvertFrom(o);
                    }
                    else
                    {
                        var ct = TypeDescriptor.GetConverter(tc);
                        if(ct.CanConvertTo(ty))
                        {
                            mapper = o => (T)ct.ConvertTo(o, ty);
                        }
                    }
                }
            }

            foreach(DataRow row in table.Rows)
            {
                var item = row[0];
                if(item is T)
                {
                    list.Add((T)item);
                }
                else if(item != DBNull.Value && item != null)
                {
                    list.Add(mapper(item));
                }
            }

            return list;
        }

        public static List<T> AsList<T>(this DataTable table, Dictionary<string, string> mapper = null) where T : class, new()
        {
            if(table == null)
            {
                return null;
            }

            var list = new List<T>();

            if(table.Rows.Count == 0)
            {
                return list;
            }

            var ps = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.CanWrite);
            var mapping = new List<Mapper>();

            var messages = new List<string>();
            foreach(var info in ps)
            {
                if(info.GetIndexParameters().Length > 1)
                {
                    continue;
                }

                var map = new Mapper() { Name = info.Name, Type = info.PropertyType, Property = info };
                var columnName = info.Name;
                if(mapper?.ContainsKey(info.Name) == true)
                {
                    columnName = mapper[info.Name];
                }
                if(string.IsNullOrWhiteSpace(columnName))
                {
                    continue;
                }

                if(!table.Columns.Contains(columnName))
                {
                    //var exinfo = string.Format("表格中不包含名称为 {0} 的列。", columnName);
                    //messages.Add(exinfo);
                    continue;
                }

                map.ColumnName = columnName;
                mapping.Add(map);
            }

            foreach(DataRow row in table.Rows)
            {
                var item = new T();
                foreach(var map in mapping)
                {
                    try
                    {
                        var value = row[map.ColumnName];
                        if(value != DBNull.Value)
                        {
                            value = Convert.ChangeType(value, map.Property.PropertyType);
                            map.Property.SetValue(item, value);
                        }
                    }
                    catch(System.Exception ex)
                    {
                        var exinfo = string.Format("字段 {0} 匹配失败，源字段：{1}，错误：{2}。", map.Name, map.ColumnName, ex.Message);
                        throw new Exception(exinfo, ex);
                    }
                }
                list.Add(item);
            }

            return list;
        }
        private class Mapper
        {
            public string Name { get; set; }
            public string ColumnName { get; set; }
            public Type Type { get; set; }
            public PropertyInfo Property { get; set; }
        }


        /// <summary>
        /// 将列表数据转换为datatable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data">列表数据</param>
        /// <returns></returns>
        public static DataTable ToDataTable<T>(this List<T> data)
        {
            return data.ToDataTable(null);
        }

        /// <summary>
        /// 将列表数据转换为datatable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data">列表数据</param>
        /// <returns></returns>
        public static DataTable ToDataTable<T>(this List<T> data, bool useDBNull)
        {
            return data.ToDataTable(null, useDBNull);
        }

        public static DataTable ToDataTable<T>(this List<T> data, List<string> properties)
        {
            return data.ToDataTable(properties?.ToDictionary(x => x, x => 0));
        }
        public static DataTable ToDataTable<T>(this List<T> data, List<string> properties, bool useDBNull, bool include)
        {
            return data.ToDataTable(properties?.ToDictionary(x => x, x => 0), useDBNull, include);
        }
        public static DataTable ToDataTable<T>(this List<T> data, Dictionary<string, int> properties, bool useDBNull = false)
        {
            return data.ToDataTable(properties, useDBNull, true);
        }

        /// <summary>
        /// 将列表数据转换为datatable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data">列表数据</param>
        /// <param name="properties">要转换的列名称和最大长度，长度为0的不设置</param>
        /// <param name="useDBNull">是否用DBNull代替空值</param>
        /// <param name="include">properties是包含还是排除</param>
        /// <returns></returns>
        public static DataTable ToDataTable<T>(this List<T> data, Dictionary<string, int> properties, bool useDBNull, bool include)
        {
            var table = new DataTable();
            var type = typeof(T);
            table.TableName = type.Name;
            var ps = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.CanRead);

            if(properties != null && properties.Count != 0)
            {
                if(include)
                {
                    ps = ps.Where(x => properties.Keys.Contains(x.Name));
                }
                else
                {
                    ps = ps.Where(x => !properties.Keys.Contains(x.Name));
                }
            }
            var mapper = new Dictionary<string, PropertyInfo>();
            foreach(var pi in ps)
            {
                var ptype = pi.PropertyType.GetNonNullableType();
                var typeCode = Type.GetTypeCode(ptype);
                if(typeCode == TypeCode.Char || typeCode == TypeCode.String)
                {
                    ptype = typeof(string);
                }
                var intTypeCode = (int)typeCode;
                if(intTypeCode < 3 || intTypeCode > 18)
                {
                    continue;
                }
                var column = new DataColumn(pi.Name, ptype);
                if(include)
                {
                    if(properties?.ContainsKey(pi.Name) == true)
                    {
                        var len = properties[pi.Name];
                        if(len > 0)
                        {
                            column.MaxLength = len;
                        }
                    }
                }
                table.Columns.Add(column);
                mapper.Add(pi.Name, pi);
            }

            foreach(var item in data)
            {
                var row = table.NewRow();
                foreach(DataColumn column in table.Columns)
                {
                    var val = mapper[column.ColumnName].GetValue(item);
                    if(useDBNull && val == null)
                    {
                        val = DBNull.Value;
                    }
                    row[column.ColumnName] = val;
                }
                table.Rows.Add(row);
            }
            return table;
        }
        /// <summary>
        /// 返回一个类型的不可空类型
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static Type GetNonNullableType(this Type type)
        {
            if(IsNullableType(type))
                return type.GetGenericArguments()[0];

            return type;
        }

        /// <summary>
        /// 判断一个类型是空或void
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static bool IsNullOrVoid(this Type type)
        {
            return type == null || type == typeof(void);
        }

        /// <summary>
        /// 判断一个类型是Nullable
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static bool IsNullableType(this Type type)
        {
            return type != null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
        }
    }

    public static class Extensions
    {
        public static bool IsEmptyOrLike(this string value, string value2)
        {
            if(string.IsNullOrWhiteSpace(value))
                return true;
            if(value2.Contains(value.Trim(), StringComparison.OrdinalIgnoreCase))
                return true;
            return false;
        }
    }
}
